package com.atguigu.tingshu.payment.service.impl;


import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;
    @Resource
    private OrderInfoFeignClient orderInfoFeignClient;
    @Autowired
    private RSAAutoCertificateConfig rsaAutoCertificateConfig ;
    @Autowired
    private WxPayV3Config wxPayV3Config;
    @Resource
    private UserInfoFeignClient userInfoFeignClient;

    //	支付文档入口：https://developers.weixin.qq.com/miniprogram/dev/api/payment/wx.requestPayment.html
    //	对接方式：https://github.com/wechatpay-apiv3/wechatpay-java
    @Override
    public Map createJsapi(String paymentType, String orderNo, Long userId) {
        try {
//	根据业务判断类型：
            //	充值业务不需要判断当前订单是否取消
            if (!paymentType.equals(SystemConstant.PAYMENT_TYPE_RECHARGE)){
                //	查询当前订单状态，如果订单状态已经取消，则不能生成二维码！
                Result<OrderInfo> orderInfoResult = this.orderInfoFeignClient.getOrderInfo(orderNo);
                OrderInfo orderInfo = orderInfoResult.getData();
                if (SystemConstant.ORDER_STATUS_CANCEL.equals(orderInfo.getOrderStatus())){
                    //	此时订单已取消，不能生成二维码！
                    return null;
                }
            }
            // 保存支付记录
            PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo);
            //	构建service
            JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
            PrepayRequest request = new PrepayRequest();
            // 注意在这个包下 com.wechat.pay.java.service.payments.jsapi.model.Amount
            Amount amount = new Amount();
            amount.setTotal(1);
            request.setAmount(amount);
            request.setAppid(wxPayV3Config.getAppid());
            request.setMchid(wxPayV3Config.getMerchantId());
            request.setDescription(paymentInfo.getContent());
            request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
            request.setOutTradeNo(paymentInfo.getOrderNo());
            //	获取用户信息
            Result<UserInfo> userInfoVoResult = userInfoFeignClient.getUserInfo(paymentInfo.getUserId());
            Assert.notNull(userInfoVoResult,"返回用户结果集对象不能为空");
            UserInfo userInfo = userInfoVoResult.getData();
            Assert.notNull(userInfo,"用户对象不能为空");
            String openid = userInfo.getWxOpenId();
            Payer payer = new Payer();
            payer.setOpenid(openid);
            request.setPayer(payer);

            // 调用下单方法，得到应答
            // response包含了调起支付所需的所有参数，可直接用于前端调起支付
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
            log.info("微信支付下单返回参数：{}", JSON.toJSONString(response));
            Map<String, Object> result = new HashMap();
            result.put("timeStamp", response.getTimeStamp());
            result.put("nonceStr", response.getNonceStr());
            result.put("package", response.getPackageVal());
            result.put("signType", response.getSignType());
            result.put("paySign", response.getPaySign());
            //	返回数据
            return result;
        }catch (IllegalArgumentException e){
            log.error("【微信支付】参数错误：" + e.getMessage());
            throw new GuiguException(201,"订单号不存在");
        }catch (ServiceException e){
            log.error("【微信支付】参数错误：" + e.getMessage());
            throw new GuiguException( 201, e.getMessage());
        }catch (Exception e){
            log.error("【微信支付】创建订单失败：" + e.getMessage());
            throw new GuiguException( 201, "微信下单异常");
        }
    }

    @Override
    public Transaction queryPayStatus(String orderNo) {
        try {
            // 构建service
            JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
            QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
            queryRequest.setMchid(wxPayV3Config.getMerchantId());
            queryRequest.setOutTradeNo(orderNo);

            Transaction result= service.queryOrderByOutTradeNo(queryRequest);
            log.info("【微信支付】查询订单返回参数：{}", JSON.toJSONString(result));
            return result;
        }catch (ServiceException  e){
            //API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
            log.error("【微信支付】查询订单异常：" + e.getMessage());
        }
        return null;
    }

    @Override
    public void wxnotify(HttpServletRequest request) {
        //1.回调通知的验签与解密
        //从request头信息获取参数
        //HTTP 头 Wechatpay-Signature
        // HTTP 头 Wechatpay-Nonce
        //HTTP 头 Wechatpay-Timestamp
        //HTTP 头 Wechatpay-Serial
        //HTTP 头 Wechatpay-Signature-Type
        //HTTP 请求体 body。切记使用原始报文，不要用 JSON 对象序列化后的字符串，避免验签的 body 和原文不一致。
        String signature = request.getHeader("Wechatpay-Signature");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String serial = request.getHeader("Wechatpay-Serial");
        //调用工具类来获取请求体数据
        String requestBody = PayUtil.readData(request);

        //构造RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .timestamp(timestamp)
                .nonce(nonce)
                .signature(signature)
                .body(requestBody)
                .build();

        //3.初始化NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        //4.以支付通知回调为例,验签.解密并转换成Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("【微信支付】回调通知参数：{}", JSON.toJSONString(transaction));
        if(null!= transaction && transaction.getTradeState()== Transaction.TradeStateEnum.SUCCESS){
            //5.处理业务
            paymentInfoService.updatePaymentStatus(transaction);
        }
    }

    @Override
    public Map<String, Object> createNative(String paymentType, String orderNo, Long userId) {
        try {
            //保存支付记录
            PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo);

            //构建 service
            NativePayService service = new NativePayService.Builder().config(rsaAutoCertificateConfig).build();
            //request.setXxx(val)设置所需参数，具体参数可见Request定义
            com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request =
                    new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
            com.wechat.pay.java.service.payments.nativepay.model.Amount amount =
                    new com.wechat.pay.java.service.payments.nativepay.model.Amount();
            amount.setTotal(1);
            request.setAmount(amount);
            request.setAppid(wxPayV3Config.getAppid());
            request.setMchid(wxPayV3Config.getMerchantId());
            request.setDescription(paymentInfo.getContent());
            request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
            request.setOutTradeNo(paymentInfo.getOrderNo());

            //调用下单方法，得到应答
            com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response =
                    service.prepay(request);
            //使用微信扫描code_url 对应的二维码,即可体验Native支付
            System.out.println(response.getCodeUrl());

            Map result=new HashMap();
            result.put("codeUrl", response.getCodeUrl());
            return result;
        }catch (ServiceException  e){
            log.error("【微信支付】创建订单服务失败：{}" , e.getMessage());
            throw new GuiguException( 201, e.getErrorMessage());
        }catch (IllegalArgumentException e){
            log.error("【微信支付】参数错误：{}", e.getMessage());
            throw new GuiguException( 201, "订单号不存在");
        }catch (Exception e){
            log.error("【微信支付】创建订单失败：{}" , e.getMessage());
            throw new GuiguException( 201, "微信下单异常");
        }
    }

    @Override
    public Boolean refund(String orderNo) {
        //创建退款对象
        RefundService refundService = new RefundService.Builder().config(rsaAutoCertificateConfig).build();
        //创建退款请求对象
        CreateRequest createRequest = new CreateRequest();
        createRequest.setOutTradeNo(orderNo);
        createRequest.setOutRefundNo(orderNo);
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(1L);
        amountReq.setTotal(1L);
        amountReq.setCurrency("CNY");
        createRequest.setAmount(amountReq);
        Refund refund = refundService.create(createRequest);
        //判断
        if(refund.getStatus().equals(Status.SUCCESS)){
            //关闭交易记录
            paymentInfoService.closePayment(orderNo);
            //修改订单状态
            return true;
        }
        return false;
    }
}
